'''
@author: Aurelien Deharbe


This file is part of Cute-Server.

Cute-Server is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Cute-Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Cute-Server.  If not, see <http://www.gnu.org/licenses/>.
'''

from PySide.QtCore import * #@UnusedWildImport
from PySide.QtGui import * #@UnusedWildImport
from cbridgethread import CBridgeThread
from cclientthread import CClientThread
from cuserdata import CUserData
from ctimeout import CTimeout
import json
import multiprocessing
import ccommand
import re
import os
import sys

class CSessionProcess(QObject, multiprocessing.Process):
    
    def __init__(self, sessid, servhost, servport):
        QObject.__init__(self)
        multiprocessing.Process.__init__(self)
        
        self._symbols = {}
        self._nbsymbols = 0
        self._sessid = sessid
        self._clients = {}
        self._clients_evts = {}
        self._datasigs = {}
        self._servhost = servhost
        self._servport = servport
        
        root = __file__
        if os.path.islink(root):
	    root = os.path.realpath(root)
        gf = open(os.path.dirname(os.path.abspath(root)) + "/conf/globals.list", 'r')
        auth = gf.read().split('\n')
        auth.append('json')
        gf.close()
        self._globals = {}
        glob = globals()
        for key in auth:
            if key.strip():
                self._globals[key.strip()] = glob[key.strip()]
        
    def run(self):
        self._connection = CBridgeThread(self._sessid, self._servhost, self._servport)
        self._connection.command.connect(self.onCommand)
        self._connection.script.connect(self.onScript)
        self._connection.killme.connect(self.onKillMe)
        self._connection.start()
        
        self._app = QApplication([])
        self._app.exec_()
    
    def getSessid(self):
        return self._sessid
    
    def trace(self, action, msg):
        print "[QSession " + self._sessid + "] " + action + " : " + str(msg)
    
    @Slot(str, CClientThread)
    def onCommand(self, cmd, th):
        
        self.trace("recv", cmd)
        
        # preprocessing
        if "#me#" in cmd:
            if th.getUuid() not in self._clients.keys():
                self._clients[th.getUuid()] = th
                self._clients_evts[th.getUuid()] = []
            cmd = cmd.replace("#me#", th.getUuid())

        (cmd, capt) = ccommand.capture(cmd)
        vars = ccommand.getVars(cmd)
        solvedVars = ccommand.getSolvedVars(vars, self._symbols)
        cmds = ccommand.getAllCommands(cmd, vars, solvedVars)
        ret = []
        for c in cmds:
            ret.append(self._eval(c, th, capt))
        self.trace("ret", ret)
    
        if not ret:
            ret = json.dumps(["err"])
        else:
            ret = json.dumps(ret)
        th.cmdret(ret)
        return
    
    @Slot(str, CClientThread)
    def onScript(self, script, name, th):
	
	self.trace("script", script)
	try:
            exec(script) in self._globals, locals()
            exec("CSessionProcess." + name + " = " + name)
            th.cmdret(json.dumps(["ok"]))
        except:
            th.cmdret(json.dumps(["err"]))
    
    def _eval(self, cmd, th, capt):
        self.trace("exec", cmd)
        
        sym = self._symbols
        scripts = self
        tracing = 0
        timeout = 3
        self._timeout = CTimeout(timeout)
        try:
            locenv = locals()
            locauth = ['sym', 'scripts']
            for key in locenv.keys():
                if key not in locauth:
                    del(locenv[key])
            
            if cmd[:8] == "scripts." and cmd[:18] != "scripts.subscribe(":
                tracing = 1
                self._timeout.start()
                sys.settrace(self.dbgtrace)
                result = eval(cmd, self._globals, locenv)
                sys.settrace(None)
                self._timeout.quit()
                self._timeout.wait()
            else:
                result = eval(cmd, self._globals, locenv)
        except:
            if tracing:
                sys.settrace(None)
                self._timeout.quit()
                self._timeout.wait()
            return "err"
        
        if not result:
            return "ok"
        
        if capt:
            sym[capt] = result
            if capt in self._datasigs.keys():
                self._datasigs[capt].qsdatachanged.emit()
            return "<" + capt + ">"
        
        if ccommand.isPrimitiveType(result):
            return result
        
        if result in sym.values():
            for key in sym.keys():
                if sym[key] == result:
                    if key in self._datasigs.keys():
                        self._datasigs[key].qsdatachanged.emit()
                    return "<" + key + ">"
        
        nn = "var" + str(len(sym))
        sym[nn] = result
        return "<" + nn + ">"
    
    def dbgtrace(self, frame, event, arg):
        if event == 'line':
            if self._timeout.isExpired():
                raise ValueError("Script timeout")
        return self.dbgtrace
    
    def subscribe(self, sname, event, types, thid):
        reg = re.compile(r'sym\[\"([a-z0-9]+)\"\]')
        r = reg.match(sname)
        if not r:
            raise ccommand.CCommandError("subscription")
        sname = r.group(1)
        
        th = self._clients[thid]
        tabtypes = types.split(",")
        
        ex = "@Slot(" + types + ")\n"
        funcname = thid + "_" + sname + "_" + event
        ex = ex + "def " + funcname
        if not types:
            ex = ex + "(self):\n"
        else:
            ex = ex + "(self,"
            for i in range(len(tabtypes)-1):
                ex = ex + "arg" + str(i) + ","
            ex = ex + "arg" + str(len(tabtypes)-1) + "):\n"
        
        ex = ex + "\tself._clients['" + thid + "'].slotret("
        ex = ex + "json.dumps(['<" + sname + ">', '" + event + "'"
        if types:
            for i in range(len(tabtypes)):
                ex = ex + ", arg" + str(i)
        ex = ex + "]))"
        
        print ex
        try:
            exec(ex)
            exec("CSessionProcess." + funcname + " = " + funcname)
            if event == "qsdatachanged":
                if sname not in self._datasigs.keys():
                    self._datasigs[sname] = QUserData()
                exec("self._datasigs['" + sname + "']." + event
                     + ".connect(self." + funcname + ")")
            else:
                exec("self._symbols['" + sname + "']." + event
                     + ".connect(self." + funcname + ")")
        except:
            raise ccommand.CCommandError("subscription")
        
        self._clients_evts[thid].append((sname, event))
        return "<" + sname + ">"
    
    @Slot(CClientThread)
    def onKillMe(self, client):
        id = client.getUuid()
        if id not in self._clients.keys():
            return
        del self._clients[id]
        levt = self._clients_evts[id]
        for (obj, evt) in levt:
            funcname = id + "_" + obj + "_" + evt
            self.disconnect(self._symbols[obj], SIGNAL("clicked()"), getattr(self, funcname))
            exec("del(CSessionProcess." + funcname + ")")



if __name__ == '__main__':
    print "Please use the cuteserver script to run it"
